`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    29/11/2010
// Design Name: 
// Module Name:    arbiter 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module arbiter(
	input wire clk,
	input wire reset,
	input wire wr0,wr1,wr2,wr3,
	input wire rd0,rd1,rd2,rd3,
	output wire [1:0] sel,
	output wire clk0,clk1,clk2,clk3,
	output wire wr,rd
);

	// *************************************************
	// Priorities update
	// *************************************************
	reg [1:0]prio_reg[3:0];
	reg [1:0]prio_next_3;
	reg [1:0]prio_next_2;
	reg [1:0]prio_next_1;
	reg [1:0]prio_next_0;
	reg [1:0]micro_reg[3:0];
	
	wire [1:0]prio_reg_0;
	wire [1:0]prio_reg_1;
	wire [1:0]prio_reg_2;
	wire [1:0]prio_reg_3;
	wire [1:0]micro_reg_0;
	wire [1:0]micro_reg_1;
	wire [1:0]micro_reg_2;
	wire [1:0]micro_reg_3;

   assign prio_reg_0 = prio_reg[0];
   assign prio_reg_1 = prio_reg[1];   
	assign prio_reg_2 = prio_reg[2];
	assign prio_reg_3 = prio_reg[3];
   assign micro_reg_0 = micro_reg[0];
   assign micro_reg_1 = micro_reg[1];
   assign micro_reg_2 = micro_reg[2];
   assign micro_reg_3 = micro_reg[3];
		
	// Priority of current picoblaze
	wire [1:0]P;
   
	// write and/or read requeriments
	wire [3:0]rq;
	// current picoblaze selected
   reg  [1:0]sel_reg;


	// *************************************************
	// write and read strobe to shared memory
	// *************************************************
   mux #(.N(1)) mux_wr(
		.sel(sel),
		.in0(wr0),
		.in1(wr1),
		.in2(wr2),
		.in3(wr3),
		.out(wr)
	);

   mux #(.N(1)) mux_rd(
		.sel(sel),
		.in0(rd0),
		.in1(rd1),
		.in2(rd2),
		.in3(rd3),
		.out(rd)
	);

	
	// next values of priorities
	always @(posedge clk,posedge reset) 
		if (reset) 
		  begin
		   // Priorities initialization 
  			 prio_reg[2'b00] <= 2'b00;
  			 prio_reg[2'b01] <= 2'b01;
  			 prio_reg[2'b10] <= 2'b10;
  			 prio_reg[2'b11] <= 2'b11;
  			 micro_reg[2'b00] <= 2'b00;
  			 micro_reg[2'b01] <= 2'b01;
  			 micro_reg[2'b10] <= 2'b10;
  			 micro_reg[2'b11] <= 2'b11;
		  end
		else 
		  begin
			 prio_reg[3] <= prio_next_3;
			 prio_reg[2] <= prio_next_2;
			 prio_reg[1] <= prio_next_1;
			 prio_reg[0] <= prio_next_0;
			 // Update inverse function micro()
			 micro_reg[prio_next_3] <= 2'b11;
			 micro_reg[prio_next_2] <= 2'b10;
			 micro_reg[prio_next_1] <= 2'b01;
			 micro_reg[prio_next_0] <= 2'b00;
			end
	
	// combinational part of next value
	assign P = micro_reg[sel];
		
	always @(P,sel,prio_reg_0,prio_reg_1,prio_reg_2,prio_reg_3)
	begin
		if (P==3) 
		begin
			prio_next_3 <= prio_reg_2;
			prio_next_2 <= prio_reg_1;
			prio_next_1 <= prio_reg_0;
			prio_next_0 <= sel;
		end
		else if (P==2)
		begin
			prio_next_3 <= prio_reg_3;
			prio_next_2 <= prio_reg_1;
			prio_next_1 <= prio_reg_0;
			prio_next_0 <= sel;
		end
		else if (P==1)
		begin
			prio_next_3 <= prio_reg_3;
			prio_next_2 <= prio_reg_2;
			prio_next_1 <= prio_reg_0;
			prio_next_0 <= sel;
		end
		else 
		begin
			prio_next_3 <= prio_reg_3;
			prio_next_2 <= prio_reg_2;
			prio_next_1 <= prio_reg_1;
			prio_next_0 <= prio_reg_0;
		end

	end
			
			
	// *************************************************
	// Determine priority matrix
	// *************************************************
	wire p01,p02,p03;
	assign p01 = (micro_reg[0]<micro_reg[1])?1:0;
	assign p02 = (micro_reg[0]<micro_reg[2])?1:0;
	assign p03 = (micro_reg[0]<micro_reg[3])?1:0;

	wire p10,p12,p13;
	assign p10 = (micro_reg[1]<micro_reg[0])?1:0;
	assign p12 = (micro_reg[1]<micro_reg[2])?1:0;
	assign p13 = (micro_reg[1]<micro_reg[3])?1:0;

	wire p20,p21,p23;
	assign p20 = (micro_reg[2]<micro_reg[0])?1:0;
	assign p21 = (micro_reg[2]<micro_reg[1])?1:0;
	assign p23 = (micro_reg[2]<micro_reg[3])?1:0;

	wire p30,p31,p32;
	assign p30 = (micro_reg[3]<micro_reg[0])?1:0;
	assign p31 = (micro_reg[3]<micro_reg[1])?1:0;
	assign p32 = (micro_reg[3]<micro_reg[2])?1:0;

	// *************************************************
	// Determine clk0 ... clk3
	// *************************************************
	wire rq0,rq1,rq2,rq3;
	assign rq0 = wr0|rd0;
	assign rq1 = wr1|rd1;
	assign rq2 = wr2|rd2;
	assign rq3 = wr3|rd3;
	
	assign clk0 = clk |                     (rq0 & rq1 & p01) | (rq0 & rq2 & p02) | (rq0 & rq3 & p03);
   assign clk1 = clk | (rq1 & rq0 & p10) |                     (rq1 & rq2 & p12) | (rq1 & rq3 & p13);
   assign clk2 = clk | (rq2 & rq0 & p20) | (rq2 & rq1 & p21)                     | (rq2 & rq3 & p23);
   assign clk3 = clk | (rq3 & rq0 & p30) | (rq3 & rq1 & p31) | (rq3 & rq2 & p32);

	// *************************************************
	// Determine SEL
	// *************************************************
   assign sel = sel_reg;
   assign rq = {rq3,rq2,rq1,rq0};
  
	always @(rq,micro_reg_0,micro_reg_1,micro_reg_2,micro_reg_3)
	begin
    case (rq)
      4'b0001: 
        sel_reg <= 0;
      4'b0010: 
        sel_reg <= 1;
      4'b0011:
        //sel_reg <= MayorPrio(1,0);
        if (micro_reg_1>micro_reg_0)
          sel_reg <= 1;
        else 
          sel_reg <= 0;
      4'b0100:	
        sel_reg <= 2;
      4'b0101: 
        //sel_reg <= MayorPrio(2,0);
        if (micro_reg_2>micro_reg_0)
          sel_reg <= 2;
        else 
          sel_reg <= 0;
      4'b0110: 
        //sel_reg <= MayorPrio(2,1);
        if (micro_reg_2>micro_reg_1)
          sel_reg <= 2;
        else 
          sel_reg <= 1;
      4'b0111: 
        //sel_reg <= MayorPrio(2,1,0);
        if (micro_reg_3>micro_reg_2)
          if (micro_reg_3>micro_reg_1)
            sel_reg <= 3;
          else 
            sel_reg <= 1;
        else 
          if (micro_reg_2>micro_reg_1)
            sel_reg <= 2;
          else 
            sel_reg <= 1;
      4'b1000: 
        sel_reg <= 3;
      4'b1001: 
        //sel_reg <= MayorPrio(3,0);
        if (micro_reg_3>micro_reg_0)
          sel_reg <= 3;
        else 
          sel_reg <= 0;
      4'b1010: 
        //sel_reg <= MayorPrio(3,1);
        if (micro_reg_3>micro_reg_1)
          sel_reg <= 3;
        else 
          sel_reg <= 1;
      4'b1011: 
        //sel_reg <= MayorPrio(3,1,0);
        if (micro_reg_3>micro_reg_1)
          if (micro_reg_3>micro_reg_0)
            sel_reg <= 3;
          else 
            sel_reg <= 0;
        else 
          if (micro_reg_1>micro_reg_0)
            sel_reg <= 1;
          else 
            sel_reg <= 0;
      4'b1100: 
        //sel_reg <= MayorPrio(3,2);
        if (micro_reg_3>micro_reg_2)
          sel_reg <= 3;
        else 
          sel_reg <= 2;
      4'b1101: 
        //sel_reg <= MayorPrio(3,2,0);
        if (micro_reg_3>micro_reg_2)
          if (micro_reg_3>micro_reg_0)
            sel_reg <= 3;
          else 
            sel_reg <= 0;
        else 
          if (micro_reg_2>micro_reg_0)
            sel_reg <= 2;
          else 
            sel_reg <= 0;
      4'b1110: 
        //sel_reg <= MayorPrio(3,2,1);
        if (micro_reg_3>micro_reg_2)
          if (micro_reg_3>micro_reg_1)
            sel_reg <= 3;
          else 
            sel_reg <= 1;
        else 
          if (micro_reg_2>micro_reg_1)
            sel_reg <= 2;
          else 
            sel_reg <= 1;
      4'b1111: 
        //sel_reg <= MayorPrio(3,2,1,0);
        if (micro_reg_3>micro_reg_2)
          if (micro_reg_3>micro_reg_1)
            if (micro_reg_3>micro_reg_0)
              sel_reg <= 3;
            else 
              sel_reg <= 0;
          else 
            if (micro_reg_1>micro_reg_0)
              sel_reg <= 1;
            else 
              sel_reg <= 0;
        else
          if (micro_reg_2>micro_reg_1)
            if (micro_reg_2>micro_reg_0)
              sel_reg <= 2;
            else 
              sel_reg <= 0;
          else 
            if (micro_reg_1>micro_reg_0)
              sel_reg <= 1;
            else 
              sel_reg <= 0;
      default:
  	     sel_reg <= 0;
    endcase
  end



endmodule
